package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

// SignIn struct holds all the necessary information for the sign-in process.
type SignIn struct {
	RefreshToken   string
	AccessToken    string
	NewRefreshToken string
	Phone          string
	SigninCount    int
	SigninReward   string
	Error          error
}

// TokenResponse struct for unmarshaling the token API response.
type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	UserName     string `json:"user_name"`
}

// SignInListResult struct for unmarshaling the sign-in list API response.
type SignInListResult struct {
	SignInCount int `json:"signInCount"`
}

// SignInListResponse struct for unmarshaling the sign-in list API response.
type SignInListResponse struct {
	Result SignInListResult `json:"result"`
}

// SignInRewardResult struct for unmarshaling the sign-in reward API response.
type SignInRewardResult struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// SignInRewardResponse struct for unmarshaling the sign-in reward API response.
type SignInRewardResponse struct {
	Result *SignInRewardResult `json:"result"` // Use pointer to handle null result
}

// NewSignIn creates a new SignIn instance.
func NewSignIn(refreshToken string) *SignIn {
	return &SignIn{
		RefreshToken: refreshToken,
	}
}

// getAccessToken exchanges the refresh_token for an access_token.
func (s *SignIn) getAccessToken() bool {
	payload := map[string]string{
		"grant_type":    "refresh_token",
		"refresh_token": s.RefreshToken,
	}
	jsonPayload, _ := json.Marshal(payload)

	resp, err := http.Post("https://auth.aliyundrive.com/v2/account/token", "application/json", bytes.NewBuffer(jsonPayload))
	if err != nil {
		s.Error = fmt.Errorf("failed to make request: %w", err)
		return false
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		s.Error = fmt.Errorf("failed to read response body: %w", err)
		return false
	}

	var data TokenResponse
	if err := json.Unmarshal(body, &data); err != nil {
		s.Error = fmt.Errorf("failed to unmarshal token response: %w", err)
		return false
	}

	if data.AccessToken == "" || data.RefreshToken == "" || data.UserName == "" {
		s.Error = fmt.Errorf("failed to get access token, missing parameters: %s", string(body))
		return false
	}

	s.AccessToken = data.AccessToken
	s.NewRefreshToken = data.RefreshToken
	s.Phone = data.UserName
	return true
}

// signIn performs the daily sign-in and claims the reward.
func (s *SignIn) signIn() {
	// First request: get sign-in list to know the current count
	reqBodySignInList := bytes.NewBufferString(`{"isReward":false}`)
	reqList, err := http.NewRequest("POST", "https://member.aliyundrive.com/v1/activity/sign_in_list?_rx-s=mobile", reqBodySignInList)
	if err != nil {
		s.Error = fmt.Errorf("failed to create sign-in list request: %w", err)
		return
	}
	reqList.Header.Set("Authorization", "Bearer "+s.AccessToken)
	reqList.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	respList, err := client.Do(reqList)
	if err != nil {
		s.Error = fmt.Errorf("failed to make sign-in list request: %w", err)
		return
	}
	defer respList.Body.Close()

	bodyList, err := ioutil.ReadAll(respList.Body)
	if err != nil {
		s.Error = fmt.Errorf("failed to read sign-in list response body: %w", err)
		return
	}

	var signInListData SignInListResponse
	if err := json.Unmarshal(bodyList, &signInListData); err != nil {
		s.Error = fmt.Errorf("failed to unmarshal sign-in list response: %w", err)
		return
	}

	s.SigninCount = signInListData.Result.SignInCount

	// Second request: claim sign-in reward
	payloadReward := map[string]int{
		"signInDay": s.SigninCount,
	}
	jsonPayloadReward, _ := json.Marshal(payloadReward)

	reqReward, err := http.NewRequest("POST", "https://member.aliyundrive.com/v1/activity/sign_in_reward?_rx-s=mobile", bytes.NewBuffer(jsonPayloadReward))
	if err != nil {
		s.Error = fmt.Errorf("failed to create sign-in reward request: %w", err)
		return
	}
	reqReward.Header.Set("Authorization", "Bearer "+s.AccessToken)
	reqReward.Header.Set("Content-Type", "application/json")

	respReward, err := client.Do(reqReward)
	if err != nil {
		s.Error = fmt.Errorf("failed to make sign-in reward request: %w", err)
		return
	}
	defer respReward.Body.Close()

	bodyReward, err := ioutil.ReadAll(respReward.Body)
	if err != nil {
		s.Error = fmt.Errorf("failed to read sign-in reward response body: %w", err)
		return
	}

	var signInRewardData SignInRewardResponse
	if err := json.Unmarshal(bodyReward, &signInRewardData); err != nil {
		s.Error = fmt.Errorf("failed to unmarshal sign-in reward response: %w", err)
		return
	}

	reward := "无奖励"
	if signInRewardData.Result != nil {
		reward = fmt.Sprintf("获得 %s %s", signInRewardData.Result.Name, signInRewardData.Result.Description)
	}
	s.SigninReward = reward

	fmt.Printf("签到成功, 本月累计签到 %d 天.\n", s.SigninCount)
	fmt.Printf("本次签到%s\n", reward)
}

// Run executes the sign-in process.
func (s *SignIn) Run() {
	if s.getAccessToken() {
		time.Sleep(3 * time.Second) // Wait for 3 seconds as in the Python script
		s.signIn()
	}

	if s.Error != nil {
		fmt.Printf("Error: %v\n", s.Error)
	}
	// You might want to print the new refresh token here to update it for future runs
	// fmt.Printf("New Refresh Token: %s\n", s.NewRefreshToken)
}

func main() {
	// Replace "your_refresh_token" with your actual refresh token
	refreshToken := "your_refresh_token"
	signin := NewSignIn(refreshToken)
	signin.Run()
}